import websocket
import datetime
import hashlib
import base64
import hmac
import json
from urllib.parse import urlencode
import time
import ssl
from wsgiref.handlers import format_date_time
from datetime import datetime
from time import mktime
import _thread as thread
from queue import Queue

import threading

# 创建一个事件对象


STATUS_FIRST_FRAME    = 0  # 第一帧的标识
STATUS_CONTINUE_FRAME = 1  # 中间帧标识
STATUS_LAST_FRAME     = 2  # 最后一帧的标识

class EventAdder:
    def __init__(self, sr):
        self.sr = sr

    def add_event(self, audio_chunk, status):
        # 添加事件到队列
        self.sr.add_event_to_queue(audio_chunk, status)

def add_events(sr):
    # 在另一个线程中实时添加事件到队列
    while True:
        # 添加事件到队列
        audio_chunk = b"your_audio_data"
        status = 0  # 0表示第一帧，1表示中间帧，2表示最后一帧
        sr.add_event_to_queue(audio_chunk, status)
        time.sleep(0.04)  # 模拟一段时间后再添加下一个事件


class SpeechRecognition:
    def __init__(self, app_id, api_key, api_secret,frame_len,frame_rate,frame_intervel):
        self.app_id = app_id
        self.api_key = api_key
        self.api_secret = api_secret
        self.ws = None
         # 公共参数(common)
        self.CommonArgs = {"app_id": self.app_id}
        # 业务参数(business)，更多个性化参数可在官网查看
        self.BusinessArgs = {"domain": "iat", "language": "zh_cn", "accent": "mandarin", "vinfo":1,"vad_eos":10000,"speex_size":20,}
        
        self.audio_data = b''
        self.send_event = threading.Event()
        self.send_thread = None  # 用于发送数据的线程
        self.event_queue = Queue()  # 初始化事件队列
        self.event = None
        self.send_param = None
        self.result = ""
        self.frame_len = frame_len
        self.frame_data = b''
        self.frame_rate = frame_rate
        self.frame_intervel = frame_intervel
        self.status = STATUS_FIRST_FRAME
    def _create_url(self):
        url = 'wss://ws-api.xfyun.cn/v2/iat'
        # 生成鉴权参数
        # 生成RFC1123格式的时间戳
        now = datetime.now()
        date = format_date_time(mktime(now.timetuple()))

        # 拼接字符串
        signature_origin = "host: " + "ws-api.xfyun.cn" + "\n"
        signature_origin += "date: " + date + "\n"
        signature_origin += "GET " + "/v2/iat " + "HTTP/1.1"
        # 进行hmac-sha256进行加密
        signature_sha = hmac.new(self.api_secret.encode('utf-8'), signature_origin.encode('utf-8'),
                                 digestmod=hashlib.sha256).digest()
        signature_sha = base64.b64encode(signature_sha).decode(encoding='utf-8')

        authorization_origin = "api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"" % (
            self.api_key, "hmac-sha256", "host date request-line", signature_sha)
        authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8')
        # 将请求的鉴权参数组合为字典
        v = {
            "authorization": authorization,
            "date": date,
            "host": "ws-api.xfyun.cn"
        }
        # 拼接鉴权参数，生成url
        url = url + '?' + urlencode(v)
        # print("date: ",date)
        # print("v: ",v)
        # 此处打印出建立连接时候的url,参考本demo的时候可取消上方打印的注释，比对相同参数时生成的url与自己代码生成的url是否一致
        # print('websocket url :', url)
        return url
# 收到websocket消息的处理
    def _on_message(self, ws, message):
        try:
            code = json.loads(message)["code"]
            if code != 0:
                errMsg = json.loads(message)["message"]
                print("Call error:", errMsg)
            else:
                data = json.loads(message)["data"]["result"]["ws"]
                for i in data:
                    for w in i["cw"]:
                        self.result += w["w"]
        except Exception as e:
            print("Receive msg, but parse exception:", e)

    def _on_error(self, ws, error):
        print("WebSocket error:", error)

    def _on_close(self, ws, error,arg):
        print("WebSocket closed",self.result)
    # 发送音频数据块的函数
    def send_audio_event(self,status, audio_data):
        #print("send_audio_event")
        self.status = status
        self.audio_data = audio_data  # 存储参数
        #self.send_audio_data(status,audio_data)
        self.add_event_to_queue(status,audio_data)
        #self.send_next_event()  # 添加这一行，以立即发送事件
        self.send_event.set()  # 设置事件

    def send_audio_data(self,status, audio_data):
        self.status = status
        self.audio_data = audio_data  # 存储参数
        if self.status == STATUS_FIRST_FRAME:
                        d = {"common": self.CommonArgs,
                            "business": self.BusinessArgs,
                            "data": {"status": 0, "format": "audio/L16;rate="+ str(self.frame_rate),
                                    "audio": str(base64.b64encode(self.audio_data), 'utf-8'),
                                    "encoding": "raw"}}
                        d = json.dumps(d)
                        self.ws.send(d)
                        print("send STATUS_FIRST_FRAME")
                        #self.status = STATUS_CONTINUE_FRAME
                    # 中间帧处理
        elif self.status == STATUS_CONTINUE_FRAME:
                        d = {"data": {"status": 1,  "format": "audio/L16;rate="+ str(self.frame_rate),
                                    "audio": str(base64.b64encode(self.audio_data), 'utf-8'),
                                    "encoding": "raw"}}
                        self.ws.send(json.dumps(d))
                        print("send STATUS_CONTINUE_FRAME")
                    # 最后一帧处理
        elif self.status == STATUS_LAST_FRAME:
                        d = {"data": {"status": 2,  "format":"audio/L16;rate="+ str(self.frame_rate),
                                    "audio": str(base64.b64encode(self.audio_data), 'utf-8'),
                                    "encoding": "raw"}}
                        self.ws.send(json.dumps(d))
                        print("send STATUS_LAST_FRAME")
                        #time.sleep(1)
                    #模拟音频采样间隔
        time.sleep(self.frame_intervel)
                    #self.send_event.wait()
                    
        

    def send_next_event(self):
        if not self.event_queue.empty():
            self.event = self.event_queue.get()  # 从队列中获取下一个事件
            print("send_next_event")
            self.send_audio_data(self.event["status"],self.event["audio_data"])
        else:
             print("is empty")

    def add_event_to_queue(self,status,audio_data):
        #print("add_event_to_queue")
        self.event = {"audio_data": audio_data, "status": status}
        self.event_queue.put(self.event)  # 将事件加入队列


    def send_audio_thread(self):
         while self.ws.connected:
                pass
            #self.send_event.wait()  # 等待事件被设置
            #self.send_event.clear()  # 清除事件状态，以便下一次等待
            #self.send_next_event()
            # if self.event is not None:
            #     self.send_audio_data(self.event["status"], self.event["audio_data"])
            #     #print("thread")
            # else:
            #     print("No event to send")
         

    def _on_open(self, ws):

        # self.websocket_opened.set()  # 设置事件，表示连接已建立成功
        def run(*args):
        #          while True:
        #             print("connected")
        #             # self.send_event.wait()  # 等待事件被设置
        #             # self.send_event.clear()  # 清除事件状态，以便下一次等待
        #             self.send_next_event()
        #             if self.event is not None:
        #                 self.send_audio_data(self.event["status"], self.event["audio_data"])
        #             #print("thread")
        #             else:
        #                 print("No event to send")


        # thread.start_new_thread(run, ())
            #pass
         # 在连接建立时发送音频数据的第一帧
            #self.send_audio_event(STATUS_FIRST_FRAME, None)
         
        # def run(*args):
             
        #      while True:
        #             self.send_event.wait()
        #             print("event",self.status)
              
        #         # # 发送第一个事件
        #         # self.send_next_event()
        #         # # 如果队列为空，等待一段时间再检查
        #         # if self.event_queue.empty():
        #         #             time.sleep(0.1)
                         
        # audio_file = "iat_pcm_8k.pcm"         
        # self.run(audio_file)  # 在新线程中运行 run 方法
             
        # thread = threading.Thread(target=run)
        # #thread.daemon = True  # 设置为守护线程，以便程序退出时自动结束线程
        # thread.start()
             
             #self.send_audio_event(STATUS_FIRST_FRAME,None)
            #while True: 
                
            
            # self.send_event.wait()
            # print("event",self.status)
                        
                        #  # 发送第一个事件
                        # self.send_next_event()
                        # # 如果队列为空，等待一段时间再检查
                        # if self.event_queue.empty():
                        #     time.sleep(0.1)
                         
                        # 正常的逻辑代码
                   
                    #time.sleep(0.1)
                    
                #print("event")
        #thread.start_new_thread(run, ())
                        
  
            #ws.close()  
                    #等待事件，直到收到其他模块的通知
                    #print("event",self.status)
                    #self.send_event.wait()
       
            with open(self.audio_file, "rb") as fp:
            # while True:
                    #buf = fp.read(self.frame_len)
                    #if not buf:
                    #     break

                while True:
                    self.frame_data = fp.read(self.frame_len)
                    if not self.frame_data:
                        self.status = STATUS_LAST_FRAME
                    # 第一帧处理
                    # 发送第一帧音频，带business 参数
                    # appid 必须带上，只需第一帧发送
                    if self.status == STATUS_FIRST_FRAME:

                        d = {"common": self.CommonArgs,
                            "business": self.BusinessArgs,
                            "data": {"status": 0, "format": "audio/L16;rate="+ str(self.frame_rate),
                                    "audio": str(base64.b64encode(self.frame_data), 'utf-8'),
                                    "encoding": "speex"}}
                                    #"encoding": "raw"}}
                        d = json.dumps(d)
                        ws.send(d)
                        self.status = STATUS_CONTINUE_FRAME
                    # 中间帧处理
                    elif self.status == STATUS_CONTINUE_FRAME:
                        d = {"data": {"status": 1,  "format": "audio/L16;rate"+ str(self.frame_rate),
                                    "audio": str(base64.b64encode(self.frame_data), 'utf-8'),
                                    "encoding": "speex"}}
                                    #"encoding": "raw"}}
                        ws.send(json.dumps(d))
                    # 最后一帧处理
                    elif self.status == STATUS_LAST_FRAME:
                        d = {"data": {"status": 2,  "format":"audio/L16;rate="+ str(self.frame_rate),
                                    "audio": str(base64.b64encode(self.frame_data), 'utf-8'),
                                    "encoding": "speex"}}
                                    #"encoding": "raw"}}
                        ws.send(json.dumps(d))
                        time.sleep(1)
                        break
                    # 模拟音频采样间隔
                    time.sleep(self.frame_intervel)
                ws.close()
        thread.start_new_thread(run, ())        


    
            
            #with open(self.audio_file, "rb") as fp:
            # while True:
                    #buf = fp.read(self.frame_len)
                    #if not buf:
                    #     break

                #     d = {"common": {"app_id": self.app_id},
                #          "business": {"language": "zh_cn", "domain": "iat", "accent": "mandarin"},
                #          "data": {"status": 0, "format": "audio/L16;rate=16000", "audio": base64.b64encode(buf).decode('utf-8')}}
                #     ws.send(json.dumps(d))
                #     time.sleep(self.frame_intervel)
                # d = {"data": {"status": 2, "format": "audio/L16;rate=16000", "audio": "", "encoding": "raw"}}
                # ws.send(json.dumps(d))
                # ws.close()


    def _ws_thread(self):
        # 在单独的线程中启动 WebSocket 连接
        url = self._create_url()
        self.ws = websocket.WebSocket(sslopt={"cert_reqs": ssl.CERT_NONE})
        self.ws.on_message = self._on_message
        self.ws.on_error = self._on_error
        self.ws.on_close = self._on_close
        
        self.ws.on_open = self._on_open
        self.ws.connect(url)
          # 启动用于发送音频数据的线程
        # self.send_thread = threading.Thread(target=self.send_audio_thread)
        # self.send_thread.start()

        # 使用事件来等待 WebSocket 连接建立成功
        self.websocket_opened.wait()
        #  在连接关闭之前持续接收消息
        while self.ws.connected:
            self.ws.recv()

    def run(self, audio_file):
        self.audio_file = audio_file
        websocket.enableTrace(False)
        url = self._create_url()
        self.ws = websocket.WebSocketApp(url, on_message=self._on_message, on_error=self._on_error, on_close=self._on_close)
        self.ws.on_open = self._on_open
        self.ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})


        # 在一个单独的线程中运行 WebSocket 连接
        # 创建事件用于等待 WebSocket 连接建立成功
        # self.websocket_opened = threading.Event()
        # ws_thread = threading.Thread(target=self._ws_thread)
        # ws_thread.start()

        # 等待 WebSocket 连接建立成功
        # self.websocket_opened.wait()
        

    def detect_voice(self, audio_file):
        # 如果之前有 WebSocket 连接，则先关闭
        if self.ws is not None:
            self.ws.close()
        self.result = ""
        self.run(audio_file)
        return self.result

if __name__ == "__main__":
    # 测试代码
    app_id = "21fa1ed5"
    api_key = "b23ea50fd4cbaf35a9a502c47cb8cd4c"
    api_secret = "YzFlNmFiMjJkYTIwMTFhNWZkYjg4NDlm"
    audio_file = "iat_pcm_8k.pcm"


    frame_len = 256
    frame_rate = 8000
    frame_intervel = 0.04

    sr = SpeechRecognition(app_id, api_key, api_secret,frame_len,frame_rate,frame_intervel)
    result = sr.detect_voice(audio_file)
   
    print("Speech recognition result:", result)
